package org.example;


import com.util.FileUtil;
import com.util.LogColorful;

import java.io.*;
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Scanner;
import java.util.Timer;
import java.util.TimerTask;

public class Main {
    // 读取数据使用的内存块
    static byte[] tempbytes = new byte[5 * 1024 * 1024];
    // 预读取内存块，减少系统创建读取任务的耗时对数据计算的误差
    static byte[] preReadByte = new byte[100];

    // 报告文件
    static File report;
    static BufferedWriter writer;

    // 程序启动时间，用于计算文件存放时间
    static long startTime = System.currentTimeMillis();

    // 统计的文件数
    static long totalFile = 0;
    static long readFile = 0;
    static long skipFile = 0;

    // 一次任务的总读取文件大小和读取时间
    static long totalSize;
    static long totalTimeNano;

    // 进度条更新
    static long lastLogTime = 0;
    static String[] loadingChar = {"-", "\\", "|", "/"};

    // 程序入参
    static boolean includeChild = false;
    static String targetDir;

    public static void main(String[] args) {
        LogColorful.logRed("声明：本程序为硬盘文件读取速度测试软件，不会对系统中已有的文件进行更改，所有处理均离线执行。"
                + "请谨慎选择测试时的文件体积，大量的读取任务可能会导致已有问题的硬盘损坏，对此造成的损失本人概不负责");
        LogColorful.logYellow("测试文件夹中的的文件时会跳过小于1MB的程序，因为通过过小的文件计算出的读取速度误差很大");

        LogColorful.logGreen("输入0可退出程序\n");

        Scanner scanner = new Scanner(System.in);
        while (true) {
            System.out.print("\n请输入路径后回车（可拖拽文件或文件夹至本窗口）: ");
            String input = scanner.nextLine();
            if (input.equals("0")) {
                return;
            }

            // 去除前后空格和引号，路径中如果有空格，拖拽到cmd中会有引号
            input = input.trim();
            if (input.startsWith("\"")) {
                input = input.replaceAll("\"", "");
            }
            process(new String[]{input, "-r"});
        }

        // process(args);
    }

    public static void process(String[] args) {
        totalFile = 0;
        readFile = 0;
        skipFile = 0;
        lastLogTime = 0;
        includeChild = false;
        totalSize = 0;
        totalTimeNano = 0;

        for (String arg : args) {
            if (arg.equals("-r")) {
                includeChild = true;
            } else {
                targetDir = arg;
            }
        }

        File file = new File(targetDir);

        if (!file.exists()) {
            LogColorful.logRed("该路径不存在！\n");
            return;
        }

        if (file.isFile()) {
            // 单独测试，没有报告
            Timer logTimer = new Timer();
            final int[] logCount = {0};
            logTimer.schedule(new TimerTask() {
                @Override
                public void run() {
                    System.out.printf("\r%s 读取中", loadingChar[logCount[0] % 4]);
                    logCount[0]++;
                }
            }, 0, 200);

            read(file, true);

            logTimer.cancel();

            return;
        }

        if (!file.isDirectory()) {
            LogColorful.logRed("该路径不是文件或文件夹！\n");
            return;
        }

        try {
            report = FileUtil.getFile("./report/"
                    + new SimpleDateFormat("yyyy-MM-dd HH.mm.ss").format(new Date()) + ".csv");
        } catch (IOException e) {
            throw new RuntimeException(e);
        }

        try {
            if (!report.exists()) {
                report.createNewFile();
            }
            writer = new BufferedWriter(new FileWriter(report));

            writer.write("文件名,文件大小(MB),读取耗时(ms),平均速度(MB/s),文件存放时间(day)\n");
        } catch (IOException e) {
            throw new RuntimeException(e);
        }

        Timer logTimer = new Timer();
        final int[] logCount = {0};
        logTimer.schedule(new TimerTask() {
            @Override
            public void run() {
                System.out.printf("\r%s  总文件：%d    统计文件：%d    跳过文件：%d", loadingChar[logCount[0] % 4], totalFile, readFile, skipFile);
                logCount[0]++;
            }
        }, 0, 200);

        scanFile(file);

        logTimer.cancel();
        System.out.printf("\r%s  总文件：%d    统计文件：%d    跳过文件：%d", loadingChar[logCount[0] % 4], totalFile, readFile, skipFile);

        System.out.println(String.format("\n总大小%s，总用时%.1fs，平均速度%.2fMB/s",
                fileSizeToText(totalSize), totalTimeNano / (float)1000000000, totalSize * 1000 / (float) totalTimeNano));
        LogColorful.logGreen("报告导出至" + report.getName() + "\n");

        try {
            writer.close();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    public static void scanFile(File rootFile) {
        File[] listFiles = rootFile.listFiles();
        if (listFiles == null) {
            return;
        }
        for (File listFile : listFiles) {
            if (listFile.exists() && listFile.canRead()) {
                if (listFile.isDirectory() && includeChild) {
                    scanFile(listFile);
                    continue;
                }
                if (listFile.isFile()) {
                    totalFile++;
                    if (listFile.length() < 1048576) {
                        skipFile++;
                        continue;
                    }
                    readFile++;
                    read(listFile, false);
                }
            }
        }
    }

    public static void read(File file, boolean single) {
        try {
            FileInputStream inputStream = new FileInputStream(file);

            inputStream.read(preReadByte);
            long start = System.nanoTime();
            // 读入多个字节到字节数组中，byteread为一次读入的字节数
            while (inputStream.read(tempbytes) != -1) {

            }
            long end = System.nanoTime();
            long time = end - start;
            inputStream.close();

            if (single) { // 测试单独文件
                System.out.printf("%n%s  文件大小:%.2fMB  用时:%.2fms  均速:%.2fMB/s%n", file.getName(), file.length() / 1048576.0, time / 1000000.0, (file.length() * 1000 / (float) time));
            } else { // 测试文件夹
                writer.write(String.format("%s,%.2f,%.2f,%.2f,%d%n",
                        "\"" + file.getPath() + "\"", file.length() / 1048576.0, time / 1000000.0,
                        (file.length() * 1000 / (float) time), Math.min((startTime - file.lastModified()) / 86400000, 1000)));

                totalSize += file.length();
                totalTimeNano += time;
            }
        } catch (Exception e) {
            return;
        }

    }

    private static String fileSizeToText(Long size) {
        Double unit = 1024D, sizeUnit = unit;
        String[] sizes = {"B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"};
        DecimalFormat df = new DecimalFormat("#.##");
        for (int i = 0; i <= sizes.length; i++) {
            if (size < sizeUnit) {
                sizeUnit = sizeUnit / unit;
                return df.format((double) size / (i == 0 ? 1 : sizeUnit)) + sizes[i];
            }
            sizeUnit = sizeUnit * unit;
        }
        return null;
    }

}